Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add HtmxorComponentResult and HtmxorHtmlRenderer #41

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

tanczosm
Copy link
Collaborator

@tanczosm tanczosm commented May 8, 2024

This is just a first effort so far, untested but I figured I would draft PR this before getting too deep.

  • Introduced HtmxorComponentEndpointHost class to act as a root component when executing a Htmxor Component endpoint.
  • Added HtmxorComponentResultExecutor
  • Created HtmxorComponentResult class that renders a Razor Component.
  • Implemented HtmxorComponentResultOfT class, which is a generic version of HtmxorComponentResult.

@tanczosm
Copy link
Collaborator Author

tanczosm commented May 8, 2024

HtmlRenderer uses HtmlRootComponent, which is public. I added HtmxorHtmlRenderer and it renders components based on HtmxorRootComponent so I had to change the visibility to public as well.

It now throws the "warning CA1815: HtmxorRootComponent should override Equals" when compiling and the checks all fail, treating that as an error.


private void BuildRenderTree(RenderTreeBuilder builder)
{
var pageLayoutType = ComponentType.GetCustomAttribute<LayoutAttribute>()?.LayoutType;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be using HtmxLayout instead if one is present?

Suggested change
var pageLayoutType = ComponentType.GetCustomAttribute<LayoutAttribute>()?.LayoutType;
// Since GetCustomAttributes does not guarantee the order of attributes
// returned, having a priority allows users to define a default HtmxLayout
// e.g. in their _Imports.razor, and then override that on a component base,
// just as they can with the @layout directive in razor files.
var pageLayoutType = ComponentLayoutType = componentType
.GetCustomAttributes<HtmxLayoutAttribute>(true)
.OrderBy(x => x.Priority)
.LastOrDefault()
?.LayoutType ??
ComponentType
.GetCustomAttribute<LayoutAttribute>()
?.LayoutType;

@egil
Copy link
Owner

egil commented May 9, 2024

HtmlRenderer uses HtmlRootComponent, which is public. I added HtmxorHtmlRenderer and it renders components based on HtmxorRootComponent so I had to change the visibility to public as well.

Why is HtmxorHtmlRenderer needed?

- Introduced `HtmxorComponentEndpointHost` class to act as a root component when executing a Htmxor Component endpoint.
- Added `HtmxorComponentResultExecutor` class to execute the result of an HTTP request in the context of an endpoint.
- Created `HtmxorComponentResult` class that renders a Razor Component.
- Implemented `HtmxorComponentResultOfT` class, which is a generic version of `HtmxorComponentResult`.
Updated the XML comments in two classes, HtmxorComponentResult and PropertyHelper. Removed the <see cref="..."/> tags for better readability and clarity. This change does not affect production code.
Significant changes include:
- Added new usings in Program.cs for Htmxor.Endpoints.Results and HtmxorExamples.Components.Pages.
- Demonstrated the mapping of a page/component in Program.cs.
- In HtmxorComponentResultExecutor.cs, refactored RenderComponentToResponse method to improve component rendering process.
- Introduced GetFormHandler method in HtmxorComponentResultExecutor.cs to handle form requests.
- Deleted the file HtmxorHtmlRenderer.cs as it was no longer needed.
- Introduced `HtmxorComponentEndpointHost` class to act as a root component when executing a Htmxor Component endpoint.
- Added `HtmxorComponentResultExecutor` class to execute the result of an HTTP request in the context of an endpoint.
- Created `HtmxorComponentResult` class that renders a Razor Component.
- Implemented `HtmxorComponentResultOfT` class, which is a generic version of `HtmxorComponentResult`.
Updated the XML comments in two classes, HtmxorComponentResult and PropertyHelper. Removed the <see cref="..."/> tags for better readability and clarity. This change does not affect production code.
@tanczosm tanczosm force-pushed the feat-htmxor-component-result branch from 2f3a04d to c72411c Compare May 14, 2024 12:23
This update refines the process of retrieving custom attributes in HtmxorComponentEndpointHost, ensuring a more reliable order by introducing a priority system. This allows users to define a default HtmxLayout and override it on a component basis.

In addition, the code in HtmxorComponentResultExecutor has been simplified by directly assigning the 'isBadRequest' variable within the conditional statement, reducing redundancy and improving readability.
@tanczosm
Copy link
Collaborator Author

tanczosm commented May 16, 2024

@egil I believe I can encapsulate most of the functionality of the htmxor renderer with the exception of rendering the App root component markup, which a call to a component renderer would not have awareness of. Since the root component typically wraps a Router component the HtmxorComponentResult has to go another way.

To get closer I think the ideal approach would require passing a root Layout and DefaultLayout to the component result renderer. This would require duplicating the markup inside App.razor though unless App.razor uses a layout itself.

The layout approach where the App root component uses a Layout that can be shared with the HtmxorComponentResult may be the cleanest approach.

app.MapGet("/greeting", () => new HtmxorComponentResult<GreetingComponent>() {
        RootLayout = typeof(AppLayout),  // optional root layout
        DefaultLayout = typeof(MainLayout)  // optional default layout if component doesn't specify a layout via Htmx attributes or @layout
    }
);

@egil
Copy link
Owner

egil commented May 17, 2024

Isn't supporting the "full page" render path irrelevant in this scenario? Just as with HtmlComponentResult, you use it because you want to render a specific component outside of the normal rendering flow afforded to you by Blazor or Htmxor.

@tanczosm
Copy link
Collaborator Author

Isn't supporting the "full page" render path irrelevant in this scenario? Just as with HtmlComponentResult, you use it because you want to render a specific component outside of the normal rendering flow afforded to you by Blazor or Htmxor.

Most likely so. I'll stick with matching the RazorComponentResult approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants